/*
 * @Author: Swance
 * @Date: 2023-12-18
 * @Description: 复制粘贴插件
 */

import { fabric } from 'fabric';
import { IEditor } from '@/types/global';
import { v4 as uuid } from 'uuid';
import { BoardSyncEvent } from '@/utils/Const.ts';

class CopyPlugin {
  canvas: fabric.Canvas;
  editor: IEditor;
  static pluginName = 'CopyPlugin';
  static apis: string[] = ['copy', 'paste'];
  public hotkeys: string[] = ['ctrl+c', 'ctrl+v'];
  private cache: null | fabric.ActiveSelection | fabric.Object;

  constructor(canvas: fabric.Canvas, editor: IEditor) {
    this.canvas = canvas;
    this.editor = editor;
    this.cache = null;
  }

  // 克隆粘贴多个对象
  _cloneActiveSelection(activeObject: fabric.Object) {
    // 粘贴后的元素间隔
    const grid = 10;
    const { canvas } = this;
    activeObject.clone((cloneObject: fabric.Object) => {
      cloneObject.clone((cloneAaginObj: fabric.ActiveSelection) => {
        canvas.discardActiveObject();
        if (!cloneAaginObj.left || !cloneAaginObj.top) return;
        cloneAaginObj.canvas = canvas;
        // 设置克隆对象的位置
        cloneAaginObj.set({
          left: cloneAaginObj.left + grid,
          top: cloneAaginObj.top + grid,
          evented: true,
          id: uuid(),
        });
        // 多个对象复制需要计算 activeSelection 各个元素的绝对位置
        const matrix = cloneAaginObj.calcTransformMatrix();
        cloneAaginObj.forEachObject((obj: fabric.Object) => {
          obj.id = uuid();
          const objectPosition = { x: obj.left, y: obj.top };
          const finalPosition = fabric.util.transformPoint(
            objectPosition,
            matrix,
          );
          obj.top = finalPosition.y;
          obj.left = finalPosition.x;
          // 同步给服务端的元素对象坐标需要绝对坐标，所以先计算并同步给服务端
          this.editor.emit(BoardSyncEvent.ADD, {
            isNewAction: true,
            newElements: [obj],
          });
          // 然后恢复原本的坐标添加至本地白板
          obj.top = objectPosition.y;
          obj.left = objectPosition.x;
          canvas.add(obj);
        });
        cloneAaginObj.setCoords();
        canvas.setActiveObject(cloneAaginObj);
        canvas.requestRenderAll();
      });
    });
  }

  // 克隆粘贴单个对象
  _cloneObject(activeObject: fabric.Object) {
    // 粘贴后的元素间隔
    const grid = 10;
    const canvas = this.canvas;
    activeObject.clone((cloneObj: fabric.Object) => {
      if (!cloneObj.left || !cloneObj.top) return;
      canvas.discardActiveObject();
      // 设置克隆对象的位置
      cloneObj.set({
        left: cloneObj.left + grid,
        top: cloneObj.top + grid,
        evented: true,
        id: uuid(),
      });
      cloneObj.setCoords();
      canvas.add(cloneObj);
      this.editor.emit(BoardSyncEvent.ADD, {
        isNewAction: true,
        newElements: [cloneObj],
      });
      canvas.setActiveObject(cloneObj);
      canvas.requestRenderAll();
    });
  }

  // 粘贴
  copy(paramsActiveObeject: fabric.ActiveSelection | fabric.Object) {
    const activeObject = paramsActiveObeject;
    if (!activeObject) return;
    if (activeObject.type === 'activeSelection') {
      this._cloneActiveSelection(activeObject);
    } else {
      this._cloneObject(activeObject);
    }
  }

  // 复制
  paste() {
    this.cache = this.canvas.getActiveObject();
  }

  // 快捷键回调
  hotkeyEvent(eventName: string, e: any) {
    if (eventName === 'ctrl+c' && e.type === 'keydown') {
      this.paste();
    }
    if (eventName === 'ctrl+v' && e.type === 'keydown') {
      if (this.cache) {
        this.copy(this.cache);
      }
    }
  }

  // 右键菜单回调
  contextMenu() {
    const activeObject = this.canvas.getActiveObject();
    if (activeObject) {
      return [
        {
          text: '复制',
          hotkey: 'Ctrl+C',
          disabled: false,
          onclick: () => this.paste(),
        },
        {
          text: '粘贴',
          hotkey: 'Ctrl+V',
          disabled: false,
          onclick: () => this.copy(this.cache),
        },
      ];
    }
  }
}

export default CopyPlugin;
